package javaapplication31;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Collection;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.event.MouseInputAdapter;

import java.awt.Graphics;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Rectangle;
import java.awt.BasicStroke;

import View.VertexView;
import View.EdgeView;

import Model.Vertex;
import Model.Graph;
import Model.Edge;

import util.RandomColor;

import GraphAlgo.FindAllPathes;

interface Figure extends Shape {

    public boolean isSelected();

    public void setSelected(boolean selected);

    public abstract Point getLocation();

    public abstract void setLocation(Point location);
}

class Box extends Rectangle2D.Float implements Figure {

    private boolean selected;

    public Box(int x, int y, int w, int h) {
        super(x, y, w, h);
    }

    public boolean isSelected() {
        return selected;
    }

    public void setSelected(boolean selected) {
        this.selected = selected;
    }

    public Point getLocation() {
        return new Point(
                (int) super.getMinX(), (int) super.getMinY());
    }

    public void setLocation(Point location) {
        super.setFrame(
                location.getX(), location.getY(), getWidth(), getHeight());
    }
}

class Line extends Line2D.Float {
}

public class FigurePainter extends JComponent {

    private static final int vertexCount = 16;
    private static final int edgeCount = 8;
    private static final Color SHAPE_COLOR = new Color(100, 100, 100);
    private static final Color SELECTED_SHAPE_COLOR = new Color(150, 100, 100);
    private static final Color SELECTION_COLOR = new Color(150, 150, 250, 50);
    private static final Color SELECTION_BORDER_COLOR = new Color(10, 10, 10);
    private Vertex[] vertexes;
    private VertexView[] vertexesViews;
    private static Edge[] edges;
    private EdgeView[] edgesView;
    private Graph graph;
    private static Font font = new Font("Monospaced", Font.PLAIN, 32);

    private class MouseHandler extends MouseInputAdapter {

        public void mouseDragged(MouseEvent e) {
            if (selectionBounds == null) {
                for (Figure figure : figures) {
                    if (figure.contains(e.getPoint())) {
                        draggingFigure = figure;
                    }
                }
            }

            if (draggingFigure != null) {
                if (draggingDelta == null) {
                    for (Figure figure : figures) {
                        figure.setSelected(figure == draggingFigure);
                    }

                    Point figureLocation = draggingFigure.getLocation();
                    Point dragToLocation = e.getPoint();

                    draggingDelta = new Point(
                            dragToLocation.x - figureLocation.x,
                            dragToLocation.y - figureLocation.y);
                } else {
                    Point figureLocation = draggingFigure.getLocation();
                    Point dragToLocation = e.getPoint();

                    figureLocation.x = dragToLocation.x - draggingDelta.x;
                    figureLocation.y = dragToLocation.y - draggingDelta.y;

                    draggingFigure.setLocation(figureLocation);
                }

                repaint();
                return;
            }

            if (selectionBounds == null) {
                selectionOrigin = e.getPoint();
                selectionBounds = new Rectangle();
            } else {
                int x1 = Math.min(selectionOrigin.x, e.getX());
                int y1 = Math.min(selectionOrigin.y, e.getY());
                int x2 = Math.max(selectionOrigin.x, e.getX());
                int y2 = Math.max(selectionOrigin.y, e.getY());

                selectionBounds.setBounds(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
            }
            repaint();
        }

        public void mouseReleased(MouseEvent e) {
            draggingDelta = null;
            draggingFigure = null;
            if (selectionBounds == null) {
                return;
            }
            for (Figure figure : figures) {
                if (selectionBounds.intersects(figure.getBounds())) {
                    figure.setSelected(true);
                } else {
                    figure.setSelected(false);
                }
            }
            selectionBounds = null;
            repaint();
        }

        public void mouseClicked(MouseEvent e) {
            selectionBounds = null;
            for (Figure figure : figures) {
                figure.setSelected(
                        figure.contains(e.getPoint()));
            }
            repaint();
        }
    }
    private Collection<Figure> figures = new ArrayList<Figure>();
    private MouseHandler mouseHandler = new MouseHandler();
    private Point selectionOrigin;
    private Rectangle selectionBounds;
    private Point draggingDelta;
    private Figure draggingFigure;
    private Line2D[] line;

    public FigurePainter() {
        setOpaque(true);

        addMouseListener(mouseHandler);
        addMouseMotionListener(mouseHandler);

        Box b = new Box(20, 20, 50, 50);
        JLabel label = new JLabel(b.width + "/");
        label.setLabelFor(this);

        figures.add(
                b);

        figures.add(
                new Box(70, 20, 50, 50));
        figures.add(
                new Box(20, 320, 50, 50));
    }

    private void render(Graphics g, String s, int x, int y) {
        g.setFont(font);
        g.setColor(Color.green);
        g.drawString(s, x, y);
        g.setColor(Color.blue);
        FontMetrics fm = g.getFontMetrics();
        Rectangle r = fm.getStringBounds(s, g).getBounds();
        g.drawRect(r.x + x, r.y + y, r.width, r.height);
    }

    private void createGraph() {
        graph = new Graph();
        vertexes = new Vertex[vertexCount];
        for (int i = 0; i < vertexCount; i++) {
            vertexes[i] = new Vertex(String.valueOf(Character.toChars('a' + i)));

        }
        edges = new Edge[edgeCount + 1];
        edges[0] = new Edge(vertexes[0], vertexes[1]);
        edges[1] = new Edge(vertexes[0], vertexes[4]);
        edges[2] = new Edge(vertexes[0], vertexes[3]);
        edges[3] = new Edge(vertexes[4], vertexes[7]);
        edges[4] = new Edge(vertexes[2], vertexes[5]);
        edges[5] = new Edge(vertexes[6], vertexes[7]);
        edges[6] = new Edge(vertexes[7], vertexes[8]);
        edges[7] = new Edge(vertexes[5], vertexes[7]);
        edges[8] = new Edge(vertexes[3], vertexes[6]);
        
        graph.setEdges(edges);
    }

    private void createVertexes(Graphics2D graphics2d) {
        vertexesViews = new VertexView[vertexCount];
        int x = 20, y = 20;
        for (int j = 0, i = 0; j < 3; j++) {
            for (int k = 0; k < 3; k++, i++) {
                vertexesViews[i] = new VertexView(vertexes[i], x, y, 50, 50);
                graphics2d.setColor(SELECTED_SHAPE_COLOR);
                graphics2d.fill(vertexesViews[i]);
                graphics2d.setColor(SELECTION_BORDER_COLOR);
                graphics2d.draw(vertexesViews[i]);
                graphics2d.drawString(vertexes[i].getName(), (int) vertexesViews[i].getCenterX(), (int) vertexesViews[i].getCenterY());
                y += 120;
            }
            x += 120;
            y = 20;
        }
    }

    private void createEdge(Graphics2D graphics2d) {
        edgesView = new EdgeView[9];

        edgesView[0] = new EdgeView(edges[0].getSource().getName() + edges[0].getTarget().getName(), vertexesViews[0].getCenterX(), vertexesViews[0].getCenterY() + 30,
                vertexesViews[1].getCenterX(), vertexesViews[1].getCenterY() - 30);
        edges[0].setView(edgesView[0]);
        edgesView[1] = new EdgeView(edges[1].getSource().getName() + edges[1].getTarget().getName(), vertexesViews[0].getMaxX(), vertexesViews[0].getMaxY(),
                vertexesViews[4].getX(), vertexesViews[4].getY());
        edges[1].setView(edgesView[1]);
        edgesView[2] = new EdgeView(edges[2].getSource().getName() + edges[2].getTarget().getName(), vertexesViews[0].getMaxX() + 5, vertexesViews[0].getCenterY(),
                vertexesViews[3].getX() - 5, vertexesViews[3].getCenterY());
        edges[2].setView(edgesView[2]);
        edgesView[3] = new EdgeView(edges[3].getSource().getName() + edges[3].getTarget().getName(), vertexesViews[4].getMaxX() + 5, vertexesViews[4].getCenterY(),
                vertexesViews[7].getX() - 5, vertexesViews[7].getCenterY());
        edges[3].setView(edgesView[3]);
        edgesView[4] = new EdgeView(edges[4].getSource().getName() + edges[4].getTarget().getName(), vertexesViews[2].getMaxX() + 5, vertexesViews[2].getCenterY(),
                vertexesViews[5].getX() - 5, vertexesViews[5].getCenterY());
        edges[4].setView(edgesView[4]);
        edgesView[5] = new EdgeView(edges[5].getSource().getName() + edges[5].getTarget().getName(), vertexesViews[6].getCenterX(), vertexesViews[6].getCenterY() + 30,
                vertexesViews[7].getCenterX(), vertexesViews[7].getCenterY() - 30);
        edges[5].setView(edgesView[5]);
        edgesView[6] = new EdgeView(edges[6].getSource().getName() + edges[6].getTarget().getName(), vertexesViews[7].getCenterX(), vertexesViews[7].getCenterY() + 30,
                vertexesViews[8].getCenterX(), vertexesViews[8].getCenterY() - 30);
        edges[6].setView(edgesView[6]);
        edgesView[7] = new EdgeView(edges[7].getSource().getName() + edges[7].getTarget().getName(), vertexesViews[5].getMaxX(), vertexesViews[5].getY(),
                vertexesViews[7].getX(), vertexesViews[7].getMaxY());
        edges[7].setView(edgesView[7]);
        edgesView[8] = new EdgeView(edges[2].getSource().getName() + edges[2].getTarget().getName(), vertexesViews[3].getMaxX() + 5, vertexesViews[3].getCenterY(),
                vertexesViews[6].getX() - 5, vertexesViews[6].getCenterY());
        edges[8].setView(edgesView[8]);


        BasicStroke bs = new BasicStroke(10, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL);
        graphics2d.setStroke(bs);

        for (EdgeView ev : edgesView) {
            graphics2d.draw(ev);
        }
    }

    @Override
    protected void paintComponent(Graphics graphics) {
        Graphics2D graphics2d = (Graphics2D) graphics;

        graphics2d.setColor(getBackground());
        graphics2d.fillRect(0, 0, getWidth(), getHeight());

        createGraph();
        createVertexes(graphics2d);
        createEdge(graphics2d);
        BasicStroke bs = new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL);
        graphics2d.setStroke(bs);


        FindAllPathes fap = new FindAllPathes(graph);
        ArrayList<LinkedList<Edge>> alist = fap.findAllPathes(vertexes[2], vertexes[0]);
        for (int i = 0; i < alist.size(); i++) {
//            graphics2d.setColor(RandomColor.nextColor());
            for (Edge e : alist.get(i)) {
                System.out.println(e.getSource().getName() + " - " + e.getTarget().getName());
                graphics2d.setPaint(Color.red);
                BasicStroke bs1 = new BasicStroke(10, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL);
                graphics2d.setStroke(bs1);
                graphics2d.draw(e.getView());
            }
        }

//        graphics2d.setColor(SELECTED_SHAPE_COLOR);

//        for (int i = 2; i < 7; i++) {
//            graphics2d.setPaint(Color.red);
//            BasicStroke bs1 = new BasicStroke(10, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL);
//            graphics2d.setStroke(bs1);
//            graphics2d.draw(edgesView[i]);
//        }

//        for (Figure figure : figures) {
//            if (figure.isSelected()) {
//                graphics2d.drawString(figure.toString(), (int) figure.getBounds2D().getX(), (int) figure.getBounds2D().getY());
//                graphics2d.setColor(SELECTED_SHAPE_COLOR);
//                graphics2d.fill(figure);
//                graphics2d.setColor(SELECTION_BORDER_COLOR);
//                graphics2d.draw(figure);
//            } else {
//                graphics2d.setColor(SHAPE_COLOR);
//                graphics2d.fill(figure);
//            }
//        }
//
//        if (selectionBounds != null) {
//            graphics2d.setColor(SELECTION_COLOR);
//            graphics2d.fillRect(selectionBounds.x, selectionBounds.y, selectionBounds.width, selectionBounds.height);
//
//            graphics2d.setColor(SELECTION_BORDER_COLOR);
//            graphics2d.drawRect(selectionBounds.x, selectionBounds.y, selectionBounds.width, selectionBounds.height);
//        }
    }

    public static void main(String... arguments) {
        JFrame frame = new JFrame();
        FigurePainter painter = new FigurePainter();

        frame.setSize(400, 420);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(painter);
        frame.setVisible(true);

    }
}